package org.sample.springboot.validator;

import java.lang.reflect.Field;
import java.util.function.Predicate;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.sample.springboot.util.ReflectUtils;
import org.sample.springboot.util.ReflectUtils.ReflectResult;
import org.sample.springboot.validator.constraints.optional.annotation.AnnotationOptionalConstraint;
import org.sample.springboot.validator.constraints.optional.annotation.EnableMark;
import org.sample.springboot.validator.constraints.optional.annotation.OptionalMark;

public class AnnotationOptionalValidator extends OptionalValidator
		implements ConstraintValidator<AnnotationOptionalConstraint, Object> {

	private boolean enableValue;

	private boolean enableForNull;

	private String name;

	@Override
	public void initialize(AnnotationOptionalConstraint constraintAnnotation) {
		enableValue = constraintAnnotation.enableValue();
		enableForNull = constraintAnnotation.enableForNull();
		name = constraintAnnotation.name();
	}

	@Override
	public boolean isValid(Object value, ConstraintValidatorContext context) {
		if (value == null) {
			return true;
		}

		Predicate<EnableMark> enableMarkChecker;
		if (name == null) {
			enableMarkChecker = mark -> mark.name() == null;
		} else {
			enableMarkChecker = mark -> name.equals(mark.name());
		}
		ReflectResult<Field> enableField = ReflectUtils.getField(value, EnableMark.class, enableMarkChecker);
		if (!enableField.isAvailable()) {
			// Fail validation if enable field doesn't exist.
			return false;
		}

		ReflectResult<Boolean> enableFieldValue = ReflectUtils.getValue(value, enableField.getResult(), Boolean.class);
		if (!enableFieldValue.isAvailable()) {
			// Fail validation if enable field isn't Boolean.
			return false;
		}

		boolean isEnabled = false;
		if (enableFieldValue.getResult() == null) {
			if (enableForNull) {
				isEnabled = true;
			}
		} else {
			if (enableFieldValue.getResult().equals(enableValue)) {
				isEnabled = true;
			}
		}

		Predicate<OptionalMark> optionalMarkChecker;
		if (name == null) {
			optionalMarkChecker = mark -> mark.name() == null;
		} else {
			optionalMarkChecker = mark -> name.equals(mark.name());
		}
		ReflectResult<Field> optionalField = ReflectUtils.getField(value, OptionalMark.class, optionalMarkChecker);
		if (!optionalField.isAvailable()) {
			// Fail validation if optional field doesn't exist.
			return false;
		}

		ReflectResult<Object> optionalFieldValue = ReflectUtils.getValue(value, optionalField.getResult());

		return isValid(isEnabled, optionalFieldValue.getResult(), optionalField.getResult().getName(), context);
	}

}
